#ifndef __DISK_H__
#define __DISK_H__

#include <stdint.h>
#include <semaphore.h>
#include <errno.h>
#include <libaio.h>

#include "ynet_rpc.h"
#include "sysy_conf.h"
#include "chunk.h"
#include "diskmd.h"
#include "plock.h"
#include "bmap.h"
#include "buffer.h"
#include "disk_sqlite3.h"

#define DISK_NONE_IDX (-1)
#define DISK_ALL_IDX (DISK_MAX+1)

#define MBR_OFFSET      0
#define MBR_SIZE        512

#define SUPER_BLOCK_OFFSET 1024

#define DISKINFO_OFFSET (4096)

#define RESERVED_ITEM_MAX_LEN 64

/*
code reconsitution to support multiple disk medium, aio, spdk, ram etc.
*/
typedef struct {
        char valid;
        diskloc_t loc;
} locs_t;

#define MAX_DISK_TYPE  2

struct disk_op_t;

typedef enum {
        __DISK_TYPE_START__ = 0xff,
        __DISK_TYPE_NORMAL_DISK__ ,
        __DISK_TYPE_SPDK_DISK__ ,
        __DISK_TYPE_RAM_DISK__,
        __DISK_TYPE_NVME_DISK__,
        __DISK_TYPE_END__,
} disk_type_t;

typedef struct {
        struct list_head hook;
        diskloc_t loc;
} delete_request_t;

typedef struct __disk_t {
        sy_rwlock_t lock;
        plock_t plock;
        int private;
        bmap_t bmap;
        int map_fd;
        int map_size;

        int idx;
        int tier;
        int status;
        int latency;
        void *disk_fd;
        disk_type_t disk_type;
        struct disk_op_t *dop;

        sem_t *sem;

        int cache;              /* cache percent */
        int cached;             /* cache is used */
        char cset_uuid[64];

        uint64_t size;          /* tier used size */
        uint64_t real_size;     /* disk total size */
        uint64_t disk_base_offset; /*disk base offset*/

        sy_spinlock_t delete_lock;
        struct list_head delete_list;

} disk_t;

#define DISKINFO_OFFSET (4096)
#define DISK_WRITEABLE_OFFSET (1024 * 6)
#define BCACHE_SUPERBLOCK_LEN (8192)
/* default used the first chunk to write super block and disk info */
#define DISK_CACHE_SIZE(size, percent) _align_down((size - LICH_CHUNK_SPLIT * CHAR_BIT) * percent / 100, PAGE_SIZE)

typedef struct {
        uint32_t crc;
        uint32_t idx;
        nid_t nid;
        uuid_t diskid;
        char cluster[MAX_NAME_LEN];
} diskinfo_t;

struct disk_op_t {
        /* disk md layer. common method*/
        int (*create_new)(disk_t *disk, const char *home, const char *pool);
        int (*probe_check)(disk_t *disk, const char *home, const char *pool);

        //int (*getinfo)(const disk_t *disk, const disk_t *disk, diskinfo_t *diskinfo);
        //int (*setinfo)(const disk_t *disk, diskinfo_t *diskinfo);
        //int (*probe_check)(disk_t *disk, int cycle, int *latency);
        void (*offline)(disk_t *disk);
        int  (*writeable)(disk_t *disk);
        int  (*destroy)(disk_t *disk);

        /* io layer. private method */
        int  (*open)(disk_t *disk, const char *home, char *pool, uint64_t *disk_size);
        void (*close)(disk_t *disk);

        /*synchronous I/O interface, possibility for metadata.*/
        int  (*io_pread)(const disk_t *disk, char *buf, size_t size, off_t offset);
        int  (*io_pwrite)(const disk_t *disk, char *buf, size_t size, off_t offset);
        /* what's it?*/
        //int  (*io_preadv)(const disk_t *disk,  buffer_t *buf, off_t offset);
        //int  (*io_pwritev)(const disk_t *disk, const buffer_t *buf, off_t offset);
        
        //int  (*aio_readv)(const disk_t *disk,  buffer_t *buf, off_t offset, int prio);
        //int  (*aio_writev)(const disk_t *disk, const buffer_t *buf, off_t _offset, int prio);
        /*neccessary interface for nvme/nvmf*/
        int  (*unmap)(const disk_t *disk, size_t size, off_t offset);

        /*asynchronous I/O interface, possibility for data I/O, buffer and offset must be aligned to the device.*/
        int  (*aio_readv)(const disk_t *disk,  const chkid_t *chkid, buffer_t *buf, off_t offset, int prio);
        int  (*aio_writev)(const disk_t *disk, const chkid_t *chkid, const buffer_t *buf, off_t _offset, int prio);
        int  (*connect)(const disk_t *disk, disk_t *new);
        void (*disconnect)(disk_t *disk);
        int (*get_size)(const char *path, int idx, uint64_t *size);
};

inline static BOOL is_aligned(const buffer_t *buf, int offset)
{
        int one_seg = 0;

        /* 一个单元 */
        if (buf->list.next->next == &buf->list)
                one_seg = 1;

        seg_t *seg = (seg_t *)buf->list.next;
        void *ptr = seg->handler.ptr;
        if (unlikely(one_seg == 0 || buf->len % 512 || offset % 512 || (uint64_t)ptr % PAGE_SIZE))
                return FALSE;
#if 1
        if (likely(((uint64_t)ptr % PAGE_SIZE == 0 &&  buf->len % PAGE_SIZE == 0))
                        || ((buf->len + (uint64_t)ptr % PAGE_SIZE) <= PAGE_SIZE)) {
                return TRUE;
        } else {
                return FALSE;
        }
#endif
        return TRUE;
}

inline static int buffer_align_trans(const buffer_t *buf, uint64_t offset, buffer_t *newbuf, uint64_t *newoff)
{
        int ret;
        uint32_t newlen;

        *newoff = round_down(offset, PAGE_SIZE);
        newlen = buf->len + (offset - *newoff);
        newlen = round_up(newlen, PAGE_SIZE);

        YASSERT(newlen <= 1024 * 1024);

        ret = mbuffer_init(newbuf, newlen);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

struct disk_op_t * get_normal_disk_ops();
struct disk_op_t * get_aio_disk_ops();
struct disk_op_t * get_spdk_disk_ops();
struct disk_op_t * get_ram_disk_ops();
struct disk_op_t * get_nvme_disk_ops();

typedef struct {
        uint64_t disk_size;
} disk_extinfo_t;

int disk_extinfo_set(const char *home, int idx, const disk_extinfo_t *disk_extinfo);
int disk_extinfo_get(const char *home, int idx, disk_extinfo_t *disk_extinfo);
void disk_extinfo_remove(const char *home, int idx);
int disk_pool_cleanup(const char *pool);

int disk_sha1(const diskloc_t *loc, char *md);
int disk_delete(disk_t *disk, const diskloc_t *loc);

/* tool */
int disk_getsize(const char *home, int idx, uint64_t *disk_size);
int disk_get_realpath(char *path, struct stat* stbuf);
int disk_getblocksize(const char *path, uint64_t *size);

/*disk_op*/
int  disk_create(int idx, disk_t **_disk);
void disk_free(disk_t **disk);

int  disk_load(disk_t *disk, const char *home, int new, char *pool, int *bad);
void disk_unload(disk_t *disk, const char *home, const char *pool);

void disk_close_bitmap(disk_t *disk);

/*disk_op extend*/
void offline(disk_t *disk);
int writeable(disk_t *disk);
int destroy(disk_t *disk);
int initnew(disk_t *disk, const char *home, const char *pool);
int normal_disk_probe_check(disk_t *disk, const char *home, const char *pool);


int disk_bmap_get_empty(disk_t *disk, locs_t *locs, int count);

int disk_setinfo(const char *home, const char *pool_name, const disk_t *disk, diskinfo_t *diskinfo);
int disk_getinfo(const char *home, const disk_t *disk, diskinfo_t *diskinfo);
void disk_unlink(const char *home, const disk_t *disk);
int disk_erasure_superblock(disk_t *disk);

int disk_getcache(disk_t *disk, int *cache);

int disk_create_bitmap_with(int diskid, uint64_t size, const void *addr);
int disk_avaiable(disk_t *disk);
int disk_allocable(disk_t *disk);
int disk_getpool(const disk_t *disk, char *name);
int disk_get_base_offset(const char *path, disk_t *disk);
int disk_exists(disk_t *disk, const diskloc_t *loc, int _exist);

#endif
